/* -*-  Mode:C; c-basic-offset:8; tab-width:8; indent-tabs-mode:t -*- */
/*
 * Copyright (c) 1999, 2000 Massachusetts Institute of Technology.
 *
 * This software is being provided by the copyright holders under the GNU
 * General Public License, either version 2 or, at your discretion, any later
 * version. (See the file `LICENSE' in the source distribution.)
 *
 * This software is provided "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. The entire risk as
 * to the quality and performance of the program is with you. See the GNU
 * General Public License for more details.
 *
 * The name and trademarks of copyright holders may NOT be used in 
 * advertising or publicity pertaining to the software without specific, 
 * written prior permission. Title to copyright in this software and any 
 * associated documentation will at all times remain with copyright 
 * holders. See the file CMAUTHORS which should have accompanied this software 
 * for a list of all copyright holders. 
 * 
 * Particular files in this distribution may be derived from previously
 * copyrighted software. This copyright applies only to those changes made by
 * the copyright holders listed in the CMAUTHORS file.
 */
/*
 * An implementation of the Congestion Manager (CM) for Linux.
 * This file implements the CM kernel API.  (The user-level API seen
 * by applications is linked from libcm, in the CM/lib/ directory.)
 */
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/malloc.h>
#include <net/sock.h>
#include <net/cm.h>
#include <net/cm_debug.h>

extern int cm_alive;

/* 
 * Initialize state for new flow and return success/fail.
 */
int cm_open(struct sock *sk)
{
	cm_macroflow_t *mflow;
	cm_flow_t *flow;
	int err;

	CM_DEBUG_ENTER();
	
	if (!sk) {
		printk("cm_open: no socket passed\n"); 
		return -1;
	}
	/*
	 * First make a flow, then find macroflow and hook it on.
	 */
	if ((flow=(cm_flow_t *) kmalloc(sizeof(cm_flow_t),GFP_KERNEL)) == NULL)
		/* XXX should I do MOD_DEC_USE_COUNT? */
		return -1;
	flow->sk = sk;
	sk->tp_cm4 = flow;
	flow->pending = CM_REQUEST_INACTIVE;
	flow->ratechange = 0;
	flow->next = NULL;
	flow->bulkcount = 0;
	flow->bulknext = NULL;
	flow->bulkprev = NULL;
	flow->cmapp_send = NULL;
	flow->cmapp_update = NULL;
	flow->type=CM_NO_BUF_ALF;
	flow->do_callback=CM_NO_RATE_CALLBACK;
	flow->downthreshmult = -1.;
	flow->upthreshmult = -1.;
	flow->lastrate = 0.;
	flow->peersk=NULL;
	flow->inflight=0;
	flow->data=NULL;
	init_timer(&flow->cm_update_timer);
	flow->cm_update_timer.expires = (unsigned long)0;
	flow->cm_update_timer.function = cm_update_timeout;
	flow->cm_update_timer.data = (unsigned long)flow;
	init_timer(&flow->cm_request_timer);
	flow->cm_request_timer.expires = (unsigned long)0;
	flow->cm_request_timer.function = cm_request_timeout;
	flow->cm_request_timer.data = (unsigned long)flow;
       

	/* 
	 * No macroflow needed for pure control flow... 
	 */	
	if (sk->daddr == NULL) 
		return 0;
	if ((mflow = cm_classify(sk)) == NULL) {
		/* 
		 * Make a new CM instance to the peer and add to cm's list.
		 */
		err = cm_make_mflow(flow);
		if (err == -1) return err;
		flow->mflow->cm_restart_timer.function = &cm_restart_timeout;
		flow->mflow->cm_restart_timer.data = (unsigned long)(flow->mflow);
		init_timer(&flow->mflow->cm_restart_timer);
	} else 
		cm_add_flow(mflow, flow);
	cm_alive = 1;
	return 0;
}

void cm_close(cm_flow_t *flow)
{
	CM_DEBUG_ENTER();
	
	if (flow->sk->daddr != 0) {
		/*
		 * Clear out balance for connected flows.
		 */
		cm_update(flow, flow->inflight, 0, 0, CM_NOLOSS);
		cm_del_flow(flow->mflow, flow);
	}
	/* delete the timers for flow */
	if (flow->cm_update_timer.prev)
		del_timer(&flow->cm_update_timer);
	if (flow->cm_request_timer.prev)
		del_timer(&flow->cm_request_timer);
	//flow->timer = (struct timer_list)0;
	kfree(flow);
	cm_alive = 0;
}

/*
 * CM ain't on the data path now.
 */
int cm_send(cm_flow_t *flow)
{
	return 0;
}

int cm_request(cm_flow_t *flow)
{
	CM_DEBUG_ENTER();
	if (flow->pending == CM_REQUEST_ACTIVE ||
	    flow->pending == CM_REQUEST_GRANTED)
		return -1;
	flow->pending = CM_REQUEST_ACTIVE;
	flow->reqtime = (__u32) jiffies; /* time when request was made */
	cmcc_wakeup(flow->mflow->congctl);
	return 0;
}

/*
 * Tests to see if it is OK to send on this socket.
 * Returns 1 if OK to send; 0 if not.
 */
int cm_ready(cm_flow_t *flow)
{
cm_flow_t *ithflow;
	CM_DEBUG_ENTER();
	if (flow->bulkcount != 0) {
		ithflow = flow->bulknext;
		while (ithflow) {
			if (ithflow->pending == CM_REQUEST_GRANTED) return 1;
			ithflow = ithflow->bulknext;
			}
		return 0;
		}
	if (flow->pending == CM_REQUEST_GRANTED) 
		{ flow->pending = CM_REQUEST_INACTIVE; return 1; }
	else return 0;
}

/*
 * Tests to see if rate has changed on this socket.
 * Returns 1 if rate changed AND CLEARS FLAG.  Returns 0 if rate unchanged.
 */
int cm_ratechange(cm_flow_t *flow)
{
cm_flow_t *ithflow;

	CM_DEBUG_ENTER();
	if (flow->bulkcount != 0) {
		ithflow = flow->bulknext;
		while (ithflow) {
			if (ithflow->ratechange) return 1;
			ithflow = ithflow->bulknext;
			}
		return 0;
		}
	if (flow->ratechange) {flow->ratechange = 0; return 1;}
	else return 0;
}

/*
 * Return current rate, rtt, and rttdev to caller.
 */
void cm_query(cm_flow_t *flow, struct cm_query *cmquery)
{
	cm_macroflow_t *mflow = flow->mflow;

	CM_DEBUG_ENTER();

	cmcc_query(mflow->congctl, &(cmquery->rate), &(cmquery->srtt),
		   &(cmquery->rttdev));
	cmquery->rate = cmquery->rate * cms_query_share(mflow->sched, flow);
	cmquery->ratechange = cm_ratechange(flow);
}

/*
 * Update information about successes, losses, and such like.
 */
void cm_update(cm_flow_t *flow, int nsent, int nrecd, int lossmode, u32 rtt)
{
	__u32 rto;
	cm_macroflow_t *mflow;
	cm_congctl_t *cmcc;
        CM_DPRINTF(CM_DEBUG_FUNCALL, "cm_debug: entering %s for %p\n",
		   __FUNCTION__, flow->mflow->congctl);
	if (flow->inflight < nsent) {
		CM_DPRINTF(CM_DEBUG_USER_ERRS,
			   "cm_debug: user error in update:  nsent %d > inflight %d\n",
			   nsent, flow->inflight);
	}
	flow->inflight = flow->inflight - nsent;
	cmcc_update(flow->mflow->congctl, nsent, nrecd, rtt, lossmode);
	/* get rto from somewhere */
	mflow = flow->mflow;
	cmcc = mflow->congctl;
	rto = (__u32)cmcc->state.rto;
	if (rto == 0)
		rto = CM_MAX_RTO;
	if (!flow->cm_update_timer.prev) {
                flow->cm_update_timer.expires = (__u32)jiffies+rto;
                add_timer(&flow->cm_update_timer);
        } else {
                if (time_before(flow->cm_update_timer.expires,(__u32)(jiffies+rto)))
			mod_timer(&flow->cm_update_timer,(__u32)jiffies+rto);
	}
}

/*
 * This should be called from right after where IP output has successfully
 * returned, for example.
 */
void cm_notify(cm_flow_t *flow, int nsent)
{
        CM_DEBUG_ENTER();

	if (!flow)
		return;
	flow->inflight += nsent;
	cmcc_notify(flow->mflow->congctl, nsent);
	cms_notify(flow->mflow->sched, flow, nsent);
	if (flow->cm_request_timer.prev)
		del_timer(&flow->cm_request_timer);
}

void cm_set_cong_param(cm_flow_t *flow, float k)
{
#ifdef CONFIG_CM_BINOMIAL
        CM_DEBUG_ENTER();

	if (!flow)
		return;
	flow->mflow->congctl->congctl_param = k;
#else
	printk("CM BINOMIAL NOT DEFINED\n");
#endif
}


#ifdef CONFIG_CM_BINOMIAL
void cm_setcongalg(cm_macroflow_t *mflow, int type)
{
	cm_mflow_setcongalg(mflow,type);
}
#endif








